# coding=utf8
import threading
from time import sleep

import pika
from bson.json_util import dumps, loads

from zUtils import config

"""
使用说明：
queue_worker = QueueWorker(self_queue_name, self_name)
queue_worker.load_actions(queue_actions_map)
queue_worker.start()

queue_worker.send(to_queue_name, to_name, action_name, data)

"""


class SocketMessage:
    def __init__(self, from_name):
        self.from_name = from_name

    def send_json(self, to_name, action, args=None):
        from bson import ObjectId
        res = {
            "action": action,
            "from": self.from_name,
            "to": to_name,
            "id": ObjectId(),
            'args': args
        }
        return res


class QueueWorker:
    internal_lock = threading.Lock()

    def __init__(self, from_name):
        queue_config = config['Queue']
        self.queue_config = {}
        for way, route_name in queue_config['Route'].items():
            self.queue_config[way] = route_name

        self.funcs = {}
        self.from_name = from_name
        self.rec_router = self.queue_config[from_name] if from_name in self.queue_config else None

        credentials = pika.PlainCredentials(queue_config['UserName'], queue_config['PassWord'])
        self.connection = pika.BlockingConnection(pika.ConnectionParameters(
            host=queue_config['Host'],
            port=queue_config['Port'],
            virtual_host=queue_config['VirtualHost'],
            credentials=credentials
        ))
        self.channel = self.connection.channel()
        # 开通持久化queue
        for queue_name in self.queue_config.values():
            self.channel.queue_declare(queue_name, durable=True)
        # 初始化socket messager
        self.messager = SocketMessage(from_name)

    def load_actions(self, action_map):
        # 有导入问题，所以改用这个函数
        self.funcs = action_map

    def start(self):
        if not self.rec_router:
            print("沒有設置監聽路徑")
            return
        thread = threading.Thread(target=self._start)
        thread.setDaemon(True)
        thread.start()
        print('开始监听queue,队列名称为{}'.format(self.rec_router))

    def _start(self):
        def callback(ch, method, properties, body):
            rec_data = loads(body.decode('utf8'))
            try:
                if rec_data['action'] not in self.funcs.keys():
                    raise KeyError('queue action don\'t have {} function'.format(rec_data['action']))
                # todo 考虑回复消息
                self.funcs[rec_data['action']](rec_data['args'])
            except Exception as e:
                print(e)
                print(rec_data)
            finally:  # 不管怎么样把消息消耗了
                ch.basic_ack(delivery_tag=method.delivery_tag)

        self.channel.basic_qos(prefetch_count=1)
        self.channel.basic_consume(callback,
                                   queue=self.rec_router)
        # self.channel.start_consuming()
        while True:
            with self.internal_lock:
                self.connection.process_data_events()
                sleep(0.01)

    def send(self, to_name, action, data_json):
        if to_name not in self.queue_config:
            raise KeyError('{router} not in config'.format(router=to_name))
        data = self.messager.send_json(to_name, action, data_json)
        self.channel.basic_publish(exchange='',
                                   routing_key=self.queue_config[to_name],
                                   body=dumps(data),
                                   properties=pika.BasicProperties(
                                       delivery_mode=2,  # make message persistent
                                   ))
